package net.cyue.nest.nestjava.common.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public final class ReflectUtil {
  public static Class<?>[] getArgTypes(Object... args) {
    if (args == null) {
      return new Class<?>[0];
    }
    Class<?>[] types = new Class<?>[args.length];
    for (int i = 0; i < args.length; i++) {
      types[i] = args[i].getClass();
    }
    return types;
  }

  public static Object invokeMethod(Object obj, String methodName, Object... args)
    throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
  {
    final var clz = obj.getClass();
    final var argTypes = ReflectUtil.getArgTypes(args);
    final var method = clz.getDeclaredMethod(methodName, argTypes);
    method.setAccessible(true);
    return method.invoke(obj, args);
  }

  public static <T> T createInstance(Class<T> clz, Object... args) {
    try {
      final var argTypes = ReflectUtil.getArgTypes(args);
      Constructor<T> constructor = clz.getDeclaredConstructor(argTypes);
      constructor.setAccessible(true);
      return constructor.newInstance(args);
    } catch (
      NoSuchMethodException |
      InvocationTargetException |
      InstantiationException |
      IllegalAccessException e
    ) {
      throw new RuntimeException(e);
    }
  }

  public static void setFieldValue(Object obj, Field field, Object value) {
    if (obj == null) {
      throw new NullPointerException("obj is null");
    }
    if (field == null) {
      throw new NullPointerException("field is null");
    }
    field.setAccessible(true);
    try {
      String fieldName = field.getName();
      String setMethodName =
        "set" +
          fieldName.substring(0, 1).toUpperCase() +
          fieldName.substring(1);
      ReflectUtil.invokeMethod(obj, setMethodName, value);
    } catch (InvocationTargetException | IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
      try {
        field.set(obj, value);
      } catch (IllegalAccessException ex) {
        throw new RuntimeException(ex);
      }
    }
  }
  public static void setFieldValue(Object obj, String fieldName, Object value) {
    try {
      Field field = obj.getClass().getField(fieldName);
      ReflectUtil.setFieldValue(obj, field, value);
    } catch (NoSuchFieldException e) {
      throw new RuntimeException(e);
    }
  }

  public static Object getFieldValue(Object obj, Field field) {
    if (obj == null) {
      throw new NullPointerException("obj is null");
    }
    if (field == null) {
      throw new NullPointerException("field is null");
    }
    field.setAccessible(true);
    try {
      String fieldName = field.getName();
      String getMethodName =
        "get" +
        fieldName.substring(0, 1).toUpperCase() +
        fieldName.substring(1);
      return ReflectUtil.invokeMethod(obj, getMethodName);
    } catch (InvocationTargetException | IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
      try {
        return field.get(obj);
      } catch (IllegalAccessException ex) {
        throw new RuntimeException(ex);
      }
    }
  }
  public static Object getFieldValue(Object obj, String fieldName) {
    try {
      Field field = obj.getClass().getField(fieldName);
      return ReflectUtil.getFieldValue(obj, field);
    } catch (NoSuchFieldException e) {
      throw new RuntimeException(e);
    }
  }
}

